using Dapper;
using Lzfy_His_Service.Models;
using Microsoft.Extensions.Options;

namespace Lzfy_His_Service.Services
{
    /// <summary>
    /// 登录失败限制服务实现
    /// </summary>
    public class LoginFailureLimitService : ILoginFailureLimitService
    {
        private readonly IOracleConnectionFactory _oracleConnectionFactory;
        private readonly ILogger<LoginFailureLimitService> _logger;
        private readonly LoginFailureLimitConfig _config;

        public LoginFailureLimitService(
            IOracleConnectionFactory oracleConnectionFactory,
            ILogger<LoginFailureLimitService> logger,
            IConfiguration configuration)
        {
            _oracleConnectionFactory = oracleConnectionFactory;
            _logger = logger;
            
            // 从配置文件读取配置，如果没有则使用默认值
            _config = new LoginFailureLimitConfig
            {
                MaxFailureCount = configuration.GetValue<int>("LoginFailureLimit:MaxFailureCount", 5),
                LockDurationMinutes = configuration.GetValue<int>("LoginFailureLimit:LockDurationMinutes", 10),
                FailureResetWindowMinutes = configuration.GetValue<int>("LoginFailureLimit:FailureResetWindowMinutes", 30)
            };
        }

        /// <summary>
        /// 检查用户是否被锁定
        /// </summary>
        public async Task<LoginFailureCheckResult> CheckUserLockStatusAsync(string userName)
        {
            try
            {
                using var connection = _oracleConnectionFactory.CreateConnection();
                connection.Open();
                
                // 首先自动解锁过期用户
                await AutoUnlockExpiredUsersAsync();
                
                var sql = @"
                    SELECT 
                        ID as Id,
                        USER_NAME as UserName,
                        IS_LOCKED as IsLocked,
                        LOCK_TIME as LockTime,
                        UNLOCK_TIME as UnlockTime,
                        FAILURE_COUNT as FailureCount,
                        LAST_FAILURE_TIME as LastFailureTime,
                        CREATED_AT as CreatedAt,
                        UPDATED_AT as UpdatedAt
                    FROM UUM.USER_LOCK_STATUS 
                    WHERE USER_NAME = :UserName";

                var lockStatus = await connection.QuerySingleOrDefaultAsync<UserLockStatus>(sql, new { UserName = userName });

                var result = new LoginFailureCheckResult
                {
                    FailureCount = lockStatus?.FailureCount ?? 0,
                    RemainingAttempts = Math.Max(0, _config.MaxFailureCount - (lockStatus?.FailureCount ?? 0))
                };

                if (lockStatus?.IsLocked == true && lockStatus.LockTime.HasValue)
                {
                    var lockExpiryTime = lockStatus.LockTime.Value.AddMinutes(_config.LockDurationMinutes);
                    var remainingMinutes = (int)Math.Ceiling((lockExpiryTime - DateTime.Now).TotalMinutes);
                    
                    if (remainingMinutes > 0)
                    {
                        result.IsLocked = true;
                        result.LockRemainingMinutes = remainingMinutes;
                        result.LockExpiryTime = lockExpiryTime;
                        result.Message = $"账户已被锁定，请在 {remainingMinutes} 分钟后重试";
                    }
                    else
                    {
                        // 锁定已过期，自动解锁
                        await UnlockUserAsync(userName);
                        result.IsLocked = false;
                        result.Message = "账户锁定已过期，已自动解锁";
                    }
                }
                else
                {
                    result.IsLocked = false;
                    if (result.FailureCount > 0)
                    {
                        result.Message = $"还可以尝试 {result.RemainingAttempts} 次";
                    }
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "检查用户锁定状态失败: UserName={UserName}", userName);
                throw;
            }
        }

        /// <summary>
        /// 记录登录失败
        /// </summary>
        public async Task<LoginFailureCheckResult> RecordLoginFailureAsync(RecordLoginFailureRequest request)
        {
            try
            {
                using var connection = _oracleConnectionFactory.CreateConnection();
                connection.Open();
                using var transaction = connection.BeginTransaction();

                try
                {
                    // 1. 记录登录失败日志
                    var insertLogSql = @"
                        INSERT INTO UUM.LOGIN_FAILURE_LOG (USER_NAME, FAILURE_TIME, IP_ADDRESS, USER_AGENT, CREATED_AT)
                        VALUES (:UserName, :FailureTime, :IpAddress, :UserAgent, :CreatedAt)";

                    await connection.ExecuteAsync(insertLogSql, new
                    {
                        UserName = request.UserName,
                        FailureTime = DateTime.Now,
                        IpAddress = request.IpAddress,
                        UserAgent = request.UserAgent,
                        CreatedAt = DateTime.Now
                    }, transaction);

                    // 2. 更新或创建用户锁定状态
                    var lockStatus = await GetUserLockStatusInternalAsync(connection, request.UserName, transaction);
                    
                    if (lockStatus == null)
                    {
                        // 创建新的锁定状态记录
                        var insertStatusSql = @"
                            INSERT INTO UUM.USER_LOCK_STATUS (USER_NAME, IS_LOCKED, FAILURE_COUNT, LAST_FAILURE_TIME, CREATED_AT, UPDATED_AT)
                            VALUES (:UserName, :IsLocked, :FailureCount, :LastFailureTime, :CreatedAt, :UpdatedAt)";

                        var newFailureCount = 1;
                        var isLocked = newFailureCount >= _config.MaxFailureCount;

                        await connection.ExecuteAsync(insertStatusSql, new
                        {
                            UserName = request.UserName,
                            IsLocked = isLocked ? 1 : 0,
                            FailureCount = newFailureCount,
                            LastFailureTime = DateTime.Now,
                            CreatedAt = DateTime.Now,
                            UpdatedAt = DateTime.Now
                        }, transaction);

                        lockStatus = new UserLockStatus
                        {
                            UserName = request.UserName,
                            IsLocked = isLocked,
                            FailureCount = newFailureCount,
                            LastFailureTime = DateTime.Now,
                            LockTime = isLocked ? DateTime.Now : null
                        };

                        if (isLocked)
                        {
                            // 设置锁定时间
                            var updateLockTimeSql = @"
                                UPDATE UUM.USER_LOCK_STATUS 
                                SET LOCK_TIME = :LockTime, UPDATED_AT = :UpdatedAt
                                WHERE USER_NAME = :UserName";

                            await connection.ExecuteAsync(updateLockTimeSql, new
                            {
                                LockTime = DateTime.Now,
                                UpdatedAt = DateTime.Now,
                                UserName = request.UserName
                            }, transaction);
                        }
                    }
                    else
                    {
                        // 更新现有记录
                        var newFailureCount = lockStatus.FailureCount + 1;
                        var isLocked = newFailureCount >= _config.MaxFailureCount;

                        var updateStatusSql = @"
                            UPDATE UUM.USER_LOCK_STATUS 
                            SET FAILURE_COUNT = :FailureCount,
                                LAST_FAILURE_TIME = :LastFailureTime,
                                IS_LOCKED = :IsLocked,
                                LOCK_TIME = :LockTime,
                                UPDATED_AT = :UpdatedAt
                            WHERE USER_NAME = :UserName";

                        await connection.ExecuteAsync(updateStatusSql, new
                        {
                            FailureCount = newFailureCount,
                            LastFailureTime = DateTime.Now,
                            IsLocked = isLocked ? 1 : 0,
                            LockTime = isLocked ? DateTime.Now : lockStatus.LockTime,
                            UpdatedAt = DateTime.Now,
                            UserName = request.UserName
                        }, transaction);

                        lockStatus.FailureCount = newFailureCount;
                        lockStatus.IsLocked = isLocked;
                        lockStatus.LastFailureTime = DateTime.Now;
                        if (isLocked && !lockStatus.LockTime.HasValue)
                        {
                            lockStatus.LockTime = DateTime.Now;
                        }
                    }

                    transaction.Commit();

                    // 3. 返回检查结果
                    var result = new LoginFailureCheckResult
                    {
                        IsLocked = lockStatus.IsLocked,
                        FailureCount = lockStatus.FailureCount,
                        RemainingAttempts = Math.Max(0, _config.MaxFailureCount - lockStatus.FailureCount)
                    };

                    if (lockStatus.IsLocked && lockStatus.LockTime.HasValue)
                    {
                        var lockExpiryTime = lockStatus.LockTime.Value.AddMinutes(_config.LockDurationMinutes);
                        var remainingMinutes = (int)Math.Ceiling((lockExpiryTime - DateTime.Now).TotalMinutes);
                        
                        result.LockRemainingMinutes = remainingMinutes;
                        result.LockExpiryTime = lockExpiryTime;
                        result.Message = $"登录失败次数过多，账户已被锁定 {_config.LockDurationMinutes} 分钟";
                    }
                    else
                    {
                        result.Message = $"登录失败，还可以尝试 {result.RemainingAttempts} 次";
                    }

                    _logger.LogWarning("用户登录失败: UserName={UserName}, FailureCount={FailureCount}, IsLocked={IsLocked}, IP={IpAddress}", 
                        request.UserName, lockStatus.FailureCount, lockStatus.IsLocked, request.IpAddress);

                    return result;
                }
                catch
                {
                    transaction.Rollback();
                    throw;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "记录登录失败时发生异常: UserName={UserName}", request.UserName);
                throw;
            }
        }

        /// <summary>
        /// 清除用户的登录失败记录（登录成功时调用）
        /// </summary>
        public async Task<bool> ClearLoginFailuresAsync(string userName)
        {
            try
            {
                using var connection = _oracleConnectionFactory.CreateConnection();
                connection.Open();
                
                var sql = @"
                    UPDATE UUM.USER_LOCK_STATUS 
                    SET FAILURE_COUNT = 0,
                        IS_LOCKED = 0,
                        LOCK_TIME = NULL,
                        UNLOCK_TIME = :UnlockTime,
                        UPDATED_AT = :UpdatedAt
                    WHERE USER_NAME = :UserName";

                var rowsAffected = await connection.ExecuteAsync(sql, new
                {
                    UnlockTime = DateTime.Now,
                    UpdatedAt = DateTime.Now,
                    UserName = userName
                });

                if (rowsAffected > 0)
                {
                    _logger.LogInformation("清除用户登录失败记录成功: UserName={UserName}", userName);
                }

                return rowsAffected > 0;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "清除用户登录失败记录时发生异常: UserName={UserName}", userName);
                throw;
            }
        }

        /// <summary>
        /// 手动解锁用户
        /// </summary>
        public async Task<bool> UnlockUserAsync(string userName)
        {
            try
            {
                using var connection = _oracleConnectionFactory.CreateConnection();
                connection.Open();
                
                var sql = @"
                    UPDATE UUM.USER_LOCK_STATUS 
                    SET IS_LOCKED = 0,
                        UNLOCK_TIME = :UnlockTime,
                        UPDATED_AT = :UpdatedAt
                    WHERE USER_NAME = :UserName AND IS_LOCKED = 1";

                var rowsAffected = await connection.ExecuteAsync(sql, new
                {
                    UnlockTime = DateTime.Now,
                    UpdatedAt = DateTime.Now,
                    UserName = userName
                });

                if (rowsAffected > 0)
                {
                    _logger.LogInformation("手动解锁用户成功: UserName={UserName}", userName);
                }

                return rowsAffected > 0;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "手动解锁用户时发生异常: UserName={UserName}", userName);
                throw;
            }
        }

        /// <summary>
        /// 自动解锁过期的用户
        /// </summary>
        public async Task<int> AutoUnlockExpiredUsersAsync()
        {
            try
            {
                using var connection = _oracleConnectionFactory.CreateConnection();
                connection.Open();
                
                var sql = @"
                    UPDATE UUM.USER_LOCK_STATUS 
                    SET IS_LOCKED = 0,
                        UNLOCK_TIME = :UnlockTime,
                        UPDATED_AT = :UpdatedAt
                    WHERE IS_LOCKED = 1 
                      AND LOCK_TIME IS NOT NULL 
                      AND LOCK_TIME + NUMTODSINTERVAL(:LockDurationMinutes, 'MINUTE') < :CurrentTime";

                var rowsAffected = await connection.ExecuteAsync(sql, new
                {
                    UnlockTime = DateTime.Now,
                    UpdatedAt = DateTime.Now,
                    LockDurationMinutes = _config.LockDurationMinutes,
                    CurrentTime = DateTime.Now
                });

                if (rowsAffected > 0)
                {
                    _logger.LogInformation("自动解锁过期用户: 解锁了 {Count} 个用户", rowsAffected);
                }

                return rowsAffected;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "自动解锁过期用户时发生异常");
                throw;
            }
        }

        /// <summary>
        /// 获取用户锁定状态详情
        /// </summary>
        public async Task<UserLockStatus?> GetUserLockStatusAsync(string userName)
        {
            try
            {
                using var connection = _oracleConnectionFactory.CreateConnection();
                connection.Open();
                
                var sql = @"
                    SELECT 
                        ID as Id,
                        USER_NAME as UserName,
                        IS_LOCKED as IsLocked,
                        LOCK_TIME as LockTime,
                        UNLOCK_TIME as UnlockTime,
                        FAILURE_COUNT as FailureCount,
                        LAST_FAILURE_TIME as LastFailureTime,
                        CREATED_AT as CreatedAt,
                        UPDATED_AT as UpdatedAt
                    FROM UUM.USER_LOCK_STATUS 
                    WHERE USER_NAME = :UserName";

                var result = await connection.QuerySingleOrDefaultAsync<UserLockStatus>(sql, new { UserName = userName });
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取用户锁定状态详情时发生异常: UserName={UserName}", userName);
                throw;
            }
        }

        /// <summary>
        /// 获取用户最近的登录失败记录
        /// </summary>
        public async Task<List<LoginFailureLog>> GetRecentLoginFailuresAsync(string userName, int hours = 24)
        {
            try
            {
                using var connection = _oracleConnectionFactory.CreateConnection();
                connection.Open();
                
                var sql = @"
                    SELECT 
                        ID as Id,
                        USER_NAME as UserName,
                        FAILURE_TIME as FailureTime,
                        IP_ADDRESS as IpAddress,
                        USER_AGENT as UserAgent,
                        CREATED_AT as CreatedAt
                    FROM UUM.LOGIN_FAILURE_LOG 
                    WHERE USER_NAME = :UserName 
                      AND FAILURE_TIME >= :StartTime
                    ORDER BY FAILURE_TIME DESC";

                var startTime = DateTime.Now.AddHours(-hours);
                var result = await connection.QueryAsync<LoginFailureLog>(sql, new 
                { 
                    UserName = userName,
                    StartTime = startTime
                });

                return result.ToList();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取用户最近登录失败记录时发生异常: UserName={UserName}", userName);
                throw;
            }
        }

        /// <summary>
        /// 清理过期的登录失败记录
        /// </summary>
        public async Task<int> CleanExpiredLoginFailuresAsync(int daysToKeep = 30)
        {
            try
            {
                using var connection = _oracleConnectionFactory.CreateConnection();
                connection.Open();
                
                var sql = @"
                    DELETE FROM UUM.LOGIN_FAILURE_LOG 
                    WHERE FAILURE_TIME < :CutoffTime";

                var cutoffTime = DateTime.Now.AddDays(-daysToKeep);
                var rowsAffected = await connection.ExecuteAsync(sql, new { CutoffTime = cutoffTime });

                if (rowsAffected > 0)
                {
                    _logger.LogInformation("清理过期登录失败记录: 删除了 {Count} 条记录", rowsAffected);
                }

                return rowsAffected;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "清理过期登录失败记录时发生异常");
                throw;
            }
        }

        /// <summary>
        /// 内部方法：获取用户锁定状态（在事务中使用）
        /// </summary>
        private async Task<UserLockStatus?> GetUserLockStatusInternalAsync(
            System.Data.IDbConnection connection, 
            string userName, 
            System.Data.IDbTransaction? transaction = null)
        {
            var sql = @"
                SELECT 
                    ID as Id,
                    USER_NAME as UserName,
                    IS_LOCKED as IsLocked,
                    LOCK_TIME as LockTime,
                    UNLOCK_TIME as UnlockTime,
                    FAILURE_COUNT as FailureCount,
                    LAST_FAILURE_TIME as LastFailureTime,
                    CREATED_AT as CreatedAt,
                    UPDATED_AT as UpdatedAt
                FROM UUM.USER_LOCK_STATUS 
                WHERE USER_NAME = :UserName";

            return await connection.QuerySingleOrDefaultAsync<UserLockStatus>(sql, new { UserName = userName }, transaction);
        }
    }
}